@raindrop-ai/opencode-plugin 0.0.9 → 0.0.11

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Raindrop AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # `@raindrop-ai/opencode-plugin`
2
+
3
+ Raindrop observability plugin for OpenCode with automatic session, event, and span tracing.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @raindrop-ai/opencode-plugin @opencode-ai/plugin
9
+ ```
10
+
11
+ If your integration also uses the OpenCode SDK directly, install `@opencode-ai/sdk` as well.
12
+
13
+ ## Notes
14
+
15
+ - `@opencode-ai/plugin` is required.
16
+ - `@opencode-ai/sdk` is an optional peer dependency.
17
+ - Tests live in the nested `tests/` package and validate HTTP payloads and tracing behavior.
package/dist/index.cjs CHANGED
@@ -66,12 +66,22 @@ function loadConfig(projectDirectory) {
66
66
  // package.json
67
67
  var package_default = {
68
68
  name: "@raindrop-ai/opencode-plugin",
69
- version: "0.0.9",
69
+ version: "0.0.11",
70
70
  description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
71
71
  type: "module",
72
72
  main: "dist/index.js",
73
73
  module: "dist/index.js",
74
74
  types: "dist/index.d.ts",
75
+ license: "MIT",
76
+ repository: {
77
+ type: "git",
78
+ url: "git+https://github.com/raindrop-ai/raindrop-js.git",
79
+ directory: "packages/opencode-plugin"
80
+ },
81
+ homepage: "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/opencode-plugin#readme",
82
+ bugs: {
83
+ url: "https://github.com/raindrop-ai/raindrop-js/issues"
84
+ },
75
85
  exports: {
76
86
  ".": {
77
87
  types: "./dist/index.d.ts",
@@ -909,6 +919,46 @@ function createSessionState(sessionId) {
909
919
  function callKey(sessionId, callId) {
910
920
  return `${sessionId}:${callId}`;
911
921
  }
922
+ function markTaskCallFinished(sessionID, callID) {
923
+ const running = runningTaskCallsBySession.get(sessionID);
924
+ if (!running) return;
925
+ running.delete(callID);
926
+ if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
927
+ }
928
+ function endPendingToolSpanWithError(state, callID, tool, toolCallArgs, errorMessage, traceShipper) {
929
+ const startInfo = state.toolSpanStarts.get(callID);
930
+ if (!startInfo) return false;
931
+ state.toolSpanStarts.delete(callID);
932
+ const error = new Error(errorMessage);
933
+ const endAttrs = [
934
+ attrString("ai.toolCall.args", toolCallArgs),
935
+ attrString("error.message", errorMessage)
936
+ ];
937
+ if (startInfo.liveSpan) {
938
+ traceShipper.endSpan(startInfo.liveSpan, {
939
+ attributes: endAttrs,
940
+ error
941
+ });
942
+ } else {
943
+ const toolSpan = traceShipper.startSpan({
944
+ name: "ai.toolCall",
945
+ parent: startInfo.parent,
946
+ eventId: startInfo.eventId,
947
+ startTimeUnixNano: startInfo.startTimeUnixNano,
948
+ attributes: [
949
+ attrString("ai.operationId", "ai.toolCall"),
950
+ attrString("ai.toolCall.name", tool),
951
+ attrString("ai.toolCall.id", callID)
952
+ ]
953
+ });
954
+ traceShipper.endSpan(toolSpan, {
955
+ attributes: endAttrs,
956
+ error,
957
+ endTimeUnixNano: nowUnixNanoString()
958
+ });
959
+ }
960
+ return true;
961
+ }
912
962
  function getHostname() {
913
963
  var _a;
914
964
  try {
@@ -993,13 +1043,13 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
993
1043
  state.outputParts.set(messageId, partObj["text"]);
994
1044
  } else if (partObj["type"] === "tool" && messageId) {
995
1045
  const tool = partObj["tool"];
1046
+ const callID = partObj["callID"];
1047
+ const partState = partObj["state"];
1048
+ if (callID == null || callID === "")
1049
+ throw new Error("message.part.updated: part.callID is required for tool part");
1050
+ if (partState == null || typeof partState !== "object")
1051
+ throw new Error("message.part.updated: part.state is required for tool part");
996
1052
  if (tool === "task") {
997
- const callID = partObj["callID"];
998
- const partState = partObj["state"];
999
- if (callID == null || callID === "")
1000
- throw new Error("message.part.updated: part.callID is required for tool part");
1001
- if (partState == null || typeof partState !== "object")
1002
- throw new Error("message.part.updated: part.state is required for tool part");
1003
1053
  const metadata = partState["metadata"];
1004
1054
  const childSessionId = metadata != null && typeof metadata === "object" ? metadata["sessionId"] : void 0;
1005
1055
  if (childSessionId != null && childSessionId !== "") {
@@ -1014,6 +1064,24 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1014
1064
  );
1015
1065
  }
1016
1066
  }
1067
+ const toolState = partState;
1068
+ if (toolState["status"] === "error") {
1069
+ const errorMessage = toolState["error"];
1070
+ if (typeof errorMessage !== "string" || errorMessage === "")
1071
+ throw new Error("message.part.updated: error tool state must have error string");
1072
+ const input = toolState["input"];
1073
+ const toolCallArgs = input === void 0 ? void 0 : typeof input === "string" ? input : JSON.stringify(input);
1074
+ if (endPendingToolSpanWithError(
1075
+ state,
1076
+ callID,
1077
+ tool,
1078
+ toolCallArgs,
1079
+ errorMessage,
1080
+ traceShipper
1081
+ ) && tool === "task") {
1082
+ markTaskCallFinished(partSessionID, callID);
1083
+ }
1084
+ }
1017
1085
  } else if (partObj["type"] === "reasoning" && typeof partObj["text"] === "string") {
1018
1086
  state.reasoningParts.set(messageId, partObj["text"]);
1019
1087
  }
@@ -1497,11 +1565,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1497
1565
  }
1498
1566
  }
1499
1567
  if (tool === "task") {
1500
- const running = runningTaskCallsBySession.get(sessionID);
1501
- if (running) {
1502
- running.delete(callID);
1503
- if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
1504
- }
1568
+ markTaskCallFinished(sessionID, callID);
1505
1569
  }
1506
1570
  } catch (err) {
1507
1571
  console.log(
package/dist/index.js CHANGED
@@ -47,12 +47,22 @@ function loadConfig(projectDirectory) {
47
47
  // package.json
48
48
  var package_default = {
49
49
  name: "@raindrop-ai/opencode-plugin",
50
- version: "0.0.9",
50
+ version: "0.0.11",
51
51
  description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
52
52
  type: "module",
53
53
  main: "dist/index.js",
54
54
  module: "dist/index.js",
55
55
  types: "dist/index.d.ts",
56
+ license: "MIT",
57
+ repository: {
58
+ type: "git",
59
+ url: "git+https://github.com/raindrop-ai/raindrop-js.git",
60
+ directory: "packages/opencode-plugin"
61
+ },
62
+ homepage: "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/opencode-plugin#readme",
63
+ bugs: {
64
+ url: "https://github.com/raindrop-ai/raindrop-js/issues"
65
+ },
56
66
  exports: {
57
67
  ".": {
58
68
  types: "./dist/index.d.ts",
@@ -890,6 +900,46 @@ function createSessionState(sessionId) {
890
900
  function callKey(sessionId, callId) {
891
901
  return `${sessionId}:${callId}`;
892
902
  }
903
+ function markTaskCallFinished(sessionID, callID) {
904
+ const running = runningTaskCallsBySession.get(sessionID);
905
+ if (!running) return;
906
+ running.delete(callID);
907
+ if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
908
+ }
909
+ function endPendingToolSpanWithError(state, callID, tool, toolCallArgs, errorMessage, traceShipper) {
910
+ const startInfo = state.toolSpanStarts.get(callID);
911
+ if (!startInfo) return false;
912
+ state.toolSpanStarts.delete(callID);
913
+ const error = new Error(errorMessage);
914
+ const endAttrs = [
915
+ attrString("ai.toolCall.args", toolCallArgs),
916
+ attrString("error.message", errorMessage)
917
+ ];
918
+ if (startInfo.liveSpan) {
919
+ traceShipper.endSpan(startInfo.liveSpan, {
920
+ attributes: endAttrs,
921
+ error
922
+ });
923
+ } else {
924
+ const toolSpan = traceShipper.startSpan({
925
+ name: "ai.toolCall",
926
+ parent: startInfo.parent,
927
+ eventId: startInfo.eventId,
928
+ startTimeUnixNano: startInfo.startTimeUnixNano,
929
+ attributes: [
930
+ attrString("ai.operationId", "ai.toolCall"),
931
+ attrString("ai.toolCall.name", tool),
932
+ attrString("ai.toolCall.id", callID)
933
+ ]
934
+ });
935
+ traceShipper.endSpan(toolSpan, {
936
+ attributes: endAttrs,
937
+ error,
938
+ endTimeUnixNano: nowUnixNanoString()
939
+ });
940
+ }
941
+ return true;
942
+ }
893
943
  function getHostname() {
894
944
  var _a;
895
945
  try {
@@ -974,13 +1024,13 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
974
1024
  state.outputParts.set(messageId, partObj["text"]);
975
1025
  } else if (partObj["type"] === "tool" && messageId) {
976
1026
  const tool = partObj["tool"];
1027
+ const callID = partObj["callID"];
1028
+ const partState = partObj["state"];
1029
+ if (callID == null || callID === "")
1030
+ throw new Error("message.part.updated: part.callID is required for tool part");
1031
+ if (partState == null || typeof partState !== "object")
1032
+ throw new Error("message.part.updated: part.state is required for tool part");
977
1033
  if (tool === "task") {
978
- const callID = partObj["callID"];
979
- const partState = partObj["state"];
980
- if (callID == null || callID === "")
981
- throw new Error("message.part.updated: part.callID is required for tool part");
982
- if (partState == null || typeof partState !== "object")
983
- throw new Error("message.part.updated: part.state is required for tool part");
984
1034
  const metadata = partState["metadata"];
985
1035
  const childSessionId = metadata != null && typeof metadata === "object" ? metadata["sessionId"] : void 0;
986
1036
  if (childSessionId != null && childSessionId !== "") {
@@ -995,6 +1045,24 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
995
1045
  );
996
1046
  }
997
1047
  }
1048
+ const toolState = partState;
1049
+ if (toolState["status"] === "error") {
1050
+ const errorMessage = toolState["error"];
1051
+ if (typeof errorMessage !== "string" || errorMessage === "")
1052
+ throw new Error("message.part.updated: error tool state must have error string");
1053
+ const input = toolState["input"];
1054
+ const toolCallArgs = input === void 0 ? void 0 : typeof input === "string" ? input : JSON.stringify(input);
1055
+ if (endPendingToolSpanWithError(
1056
+ state,
1057
+ callID,
1058
+ tool,
1059
+ toolCallArgs,
1060
+ errorMessage,
1061
+ traceShipper
1062
+ ) && tool === "task") {
1063
+ markTaskCallFinished(partSessionID, callID);
1064
+ }
1065
+ }
998
1066
  } else if (partObj["type"] === "reasoning" && typeof partObj["text"] === "string") {
999
1067
  state.reasoningParts.set(messageId, partObj["text"]);
1000
1068
  }
@@ -1478,11 +1546,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1478
1546
  }
1479
1547
  }
1480
1548
  if (tool === "task") {
1481
- const running = runningTaskCallsBySession.get(sessionID);
1482
- if (running) {
1483
- running.delete(callID);
1484
- if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
1485
- }
1549
+ markTaskCallFinished(sessionID, callID);
1486
1550
  }
1487
1551
  } catch (err) {
1488
1552
  console.log(
package/package.json CHANGED
@@ -1,11 +1,21 @@
1
1
  {
2
2
  "name": "@raindrop-ai/opencode-plugin",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "Raindrop observability plugin for OpenCode — automatic session/event/span tracing",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/raindrop-ai/raindrop-js.git",
13
+ "directory": "packages/opencode-plugin"
14
+ },
15
+ "homepage": "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/opencode-plugin#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/raindrop-ai/raindrop-js/issues"
18
+ },
9
19
  "exports": {
10
20
  ".": {
11
21
  "types": "./dist/index.d.ts",
@@ -17,12 +27,6 @@
17
27
  "files": [
18
28
  "dist/**"
19
29
  ],
20
- "scripts": {
21
- "build": "tsup",
22
- "dev": "tsup --watch",
23
- "clean": "rm -rf dist",
24
- "test": "cd tests && pnpm test"
25
- },
26
30
  "peerDependencies": {
27
31
  "@opencode-ai/plugin": ">=1.3.0",
28
32
  "@opencode-ai/sdk": ">=1.3.0"
@@ -33,12 +37,12 @@
33
37
  }
34
38
  },
35
39
  "devDependencies": {
36
- "@raindrop-ai/core": "workspace:*",
37
40
  "@opencode-ai/plugin": "^1.3.3",
38
41
  "@opencode-ai/sdk": "^1.3.3",
39
42
  "@types/node": "^20.11.17",
40
43
  "tsup": "^8.4.0",
41
- "typescript": "^5.3.3"
44
+ "typescript": "^5.3.3",
45
+ "@raindrop-ai/core": "0.0.1"
42
46
  },
43
47
  "tsup": {
44
48
  "entry": [
@@ -58,5 +62,11 @@
58
62
  },
59
63
  "publishConfig": {
60
64
  "access": "public"
65
+ },
66
+ "scripts": {
67
+ "build": "tsup",
68
+ "dev": "tsup --watch",
69
+ "clean": "rm -rf dist",
70
+ "test": "cd tests && pnpm test"
61
71
  }
62
- }
72
+ }