@raindrop-ai/opencode-plugin 0.0.5 → 0.0.6

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.
Files changed (3) hide show
  1. package/dist/index.cjs +109 -90
  2. package/dist/index.js +109 -90
  3. package/package.json +3 -3
package/dist/index.cjs CHANGED
@@ -45,14 +45,91 @@ function loadConfig(projectDirectory) {
45
45
  } catch (e) {
46
46
  }
47
47
  }
48
+ let eventMetadata;
49
+ const envMeta = process.env["RAINDROP_EVENT_METADATA"];
50
+ if (envMeta) {
51
+ try {
52
+ eventMetadata = JSON.parse(envMeta);
53
+ } catch (e) {
54
+ }
55
+ }
48
56
  return {
49
57
  writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : merged.write_key) != null ? _b : "",
50
58
  endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai/v1",
51
59
  eventName: (_e = merged.event_name) != null ? _e : "opencode_session",
52
- debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false
60
+ debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false,
61
+ eventMetadata
53
62
  };
54
63
  }
55
64
 
65
+ // package.json
66
+ var package_default = {
67
+ name: "@raindrop-ai/opencode-plugin",
68
+ version: "0.0.6",
69
+ description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
70
+ type: "module",
71
+ main: "dist/index.cjs",
72
+ module: "dist/index.js",
73
+ types: "dist/index.d.ts",
74
+ exports: {
75
+ ".": {
76
+ types: "./dist/index.d.ts",
77
+ import: "./dist/index.js",
78
+ require: "./dist/index.cjs"
79
+ }
80
+ },
81
+ sideEffects: false,
82
+ files: [
83
+ "dist/**"
84
+ ],
85
+ scripts: {
86
+ build: "tsup",
87
+ dev: "tsup --watch",
88
+ clean: "rm -rf dist",
89
+ test: "cd tests && pnpm test"
90
+ },
91
+ peerDependencies: {
92
+ "@opencode-ai/plugin": ">=1.2.0",
93
+ "@opencode-ai/sdk": ">=1.2.0"
94
+ },
95
+ peerDependenciesMeta: {
96
+ "@opencode-ai/sdk": {
97
+ optional: true
98
+ }
99
+ },
100
+ devDependencies: {
101
+ "@raindrop-ai/core": "workspace:*",
102
+ "@opencode-ai/plugin": "^1.2.0",
103
+ "@opencode-ai/sdk": "^1.2.0",
104
+ "@types/node": "^20.11.17",
105
+ tsup: "^8.4.0",
106
+ typescript: "^5.3.3"
107
+ },
108
+ tsup: {
109
+ entry: [
110
+ "src/index.ts"
111
+ ],
112
+ format: [
113
+ "cjs",
114
+ "esm"
115
+ ],
116
+ dts: {
117
+ resolve: true
118
+ },
119
+ clean: true,
120
+ noExternal: [
121
+ "@raindrop-ai/core"
122
+ ]
123
+ },
124
+ publishConfig: {
125
+ access: "public"
126
+ }
127
+ };
128
+
129
+ // src/package-info.ts
130
+ var PLUGIN_NAME = package_default.name;
131
+ var PLUGIN_VERSION = package_default.version;
132
+
56
133
  // ../core/dist/chunk-FTBZHS25.js
57
134
  function getCrypto() {
58
135
  const c = globalThis.crypto;
@@ -636,70 +713,6 @@ var TraceShipper = class {
636
713
  }
637
714
  };
638
715
 
639
- // package.json
640
- var package_default = {
641
- name: "@raindrop-ai/opencode-plugin",
642
- version: "0.0.5",
643
- description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
644
- type: "module",
645
- main: "dist/index.cjs",
646
- module: "dist/index.js",
647
- types: "dist/index.d.ts",
648
- exports: {
649
- ".": {
650
- types: "./dist/index.d.ts",
651
- import: "./dist/index.js",
652
- require: "./dist/index.cjs"
653
- }
654
- },
655
- sideEffects: false,
656
- files: [
657
- "dist/**"
658
- ],
659
- scripts: {
660
- build: "tsup",
661
- dev: "tsup --watch",
662
- clean: "rm -rf dist",
663
- test: "cd tests && yarn test"
664
- },
665
- peerDependencies: {
666
- "@opencode-ai/plugin": ">=1.2.0",
667
- "@opencode-ai/sdk": ">=1.2.0"
668
- },
669
- peerDependenciesMeta: {
670
- "@opencode-ai/sdk": {
671
- optional: true
672
- }
673
- },
674
- devDependencies: {
675
- "@raindrop-ai/core": "*",
676
- "@opencode-ai/plugin": "^1.2.0",
677
- "@opencode-ai/sdk": "^1.2.0",
678
- "@types/node": "^20.11.17",
679
- tsup: "^8.4.0",
680
- typescript: "^5.3.3"
681
- },
682
- tsup: {
683
- entry: [
684
- "src/index.ts"
685
- ],
686
- format: [
687
- "cjs",
688
- "esm"
689
- ],
690
- dts: {
691
- resolve: true
692
- },
693
- clean: true,
694
- noExternal: [
695
- "@raindrop-ai/core"
696
- ]
697
- },
698
- publishConfig: {
699
- access: "public"
700
- }
701
- };
702
-
703
716
  // src/shipper.ts
704
717
  var EventShipper2 = class extends EventShipper {
705
718
  constructor(opts) {
@@ -707,8 +720,8 @@ var EventShipper2 = class extends EventShipper {
707
720
  super({
708
721
  ...opts,
709
722
  sdkName: (_a = opts.sdkName) != null ? _a : "opencode-plugin",
710
- libraryName: (_b = opts.libraryName) != null ? _b : package_default.name,
711
- libraryVersion: (_c = opts.libraryVersion) != null ? _c : package_default.version,
723
+ libraryName: (_b = opts.libraryName) != null ? _b : PLUGIN_NAME,
724
+ libraryVersion: (_c = opts.libraryVersion) != null ? _c : PLUGIN_VERSION,
712
725
  defaultEventName: (_d = opts.defaultEventName) != null ? _d : "opencode_session"
713
726
  });
714
727
  }
@@ -720,7 +733,7 @@ var TraceShipper2 = class extends TraceShipper {
720
733
  ...opts,
721
734
  sdkName: (_a = opts.sdkName) != null ? _a : "opencode-plugin",
722
735
  serviceName: (_b = opts.serviceName) != null ? _b : "raindrop.opencode-plugin",
723
- serviceVersion: (_c = opts.serviceVersion) != null ? _c : package_default.version
736
+ serviceVersion: (_c = opts.serviceVersion) != null ? _c : PLUGIN_VERSION
724
737
  });
725
738
  }
726
739
  enqueue(span) {
@@ -820,8 +833,6 @@ var pendingChildSessionsByCallKey = /* @__PURE__ */ new Map();
820
833
  function createSessionState(sessionId) {
821
834
  return {
822
835
  sessionId,
823
- turnNumber: 0,
824
- toolCount: 0,
825
836
  currentInput: "",
826
837
  outputParts: /* @__PURE__ */ new Map(),
827
838
  reasoningParts: /* @__PURE__ */ new Map(),
@@ -875,7 +886,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
875
886
  // event — session lifecycle + streaming parts
876
887
  // ------------------------------------------------------------------
877
888
  event: async ({ event }) => {
878
- var _a, _b, _c, _d, _e;
889
+ var _a, _b, _c, _d, _e, _f, _g;
879
890
  try {
880
891
  const props = event.properties;
881
892
  const info = props["info"];
@@ -1093,12 +1104,11 @@ type: ${name != null ? name : "UnknownError"}`;
1093
1104
  }
1094
1105
  if (!state.parentId) {
1095
1106
  await eventShipper.finish(state.currentEventId, {
1096
- userId: state.sessionId,
1107
+ userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
1097
1108
  model: modelName,
1098
1109
  output: outputText,
1099
1110
  properties: {
1100
- turn_count: state.turnNumber,
1101
- tool_count: state.toolCount
1111
+ plugin_version: PLUGIN_VERSION
1102
1112
  }
1103
1113
  });
1104
1114
  }
@@ -1136,7 +1146,7 @@ type: ${name != null ? name : "UnknownError"}`;
1136
1146
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
1137
1147
  sessions.delete(String(sessionID));
1138
1148
  } else if (event.type === "session.error") {
1139
- const errorSessionID = (_c = props["sessionID"]) != null ? _c : sessionID;
1149
+ const errorSessionID = (_e = props["sessionID"]) != null ? _e : sessionID;
1140
1150
  if (errorSessionID === void 0 || errorSessionID === "")
1141
1151
  throw new Error("session.error: missing sessionID");
1142
1152
  const state = sessions.get(String(errorSessionID));
@@ -1144,7 +1154,7 @@ type: ${name != null ? name : "UnknownError"}`;
1144
1154
  const errorObj = props["error"];
1145
1155
  if (errorObj == null) throw new Error("session.error: missing error object");
1146
1156
  const errorName = errorObj.name;
1147
- const errorMessage = (_e = (_d = errorObj.data) == null ? void 0 : _d.message) != null ? _e : errorObj.name;
1157
+ const errorMessage = (_g = (_f = errorObj.data) == null ? void 0 : _f.message) != null ? _g : errorObj.name;
1148
1158
  if (errorName == null && errorMessage == null)
1149
1159
  throw new Error("session.error: error object must have name or data.message");
1150
1160
  const errorStr = `${errorMessage != null ? errorMessage : errorName}
@@ -1171,6 +1181,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1171
1181
  // chat.message — user sent a message; start a new turn with a fresh event ID
1172
1182
  // ------------------------------------------------------------------
1173
1183
  "chat.message": async (messageInput, output) => {
1184
+ var _a, _b, _c, _d, _e, _f, _g;
1174
1185
  try {
1175
1186
  const { sessionID } = messageInput;
1176
1187
  let state = sessions.get(sessionID);
@@ -1183,15 +1194,24 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1183
1194
  traceShipper.endSpan(state.currentRootSpan);
1184
1195
  state.currentRootSpan = void 0;
1185
1196
  }
1186
- state.turnNumber++;
1187
1197
  if (output == null) throw new Error("chat.message: output is required");
1188
1198
  const parts = output.parts;
1189
1199
  if (parts == null) throw new Error("chat.message: output.parts is required");
1190
1200
  const textParts = [];
1191
1201
  const newAttachments = [];
1202
+ const baseMeta = { ...config.eventMetadata };
1203
+ const promptMeta = {};
1192
1204
  for (const part of parts) {
1193
1205
  if (part["type"] === "text" && typeof part["text"] === "string") {
1194
1206
  textParts.push(part["text"]);
1207
+ const meta = part["metadata"];
1208
+ if (meta != null && typeof meta === "object") {
1209
+ const m = meta;
1210
+ if (typeof m["userId"] === "string") promptMeta.userId = m["userId"];
1211
+ if (typeof m["eventName"] === "string") promptMeta.eventName = m["eventName"];
1212
+ if (m["properties"] != null && typeof m["properties"] === "object")
1213
+ promptMeta.properties = m["properties"];
1214
+ }
1195
1215
  } else if (part["type"] === "file") {
1196
1216
  const mimeType = part["mediaType"];
1197
1217
  const filename = part["filename"];
@@ -1203,6 +1223,11 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1203
1223
  }
1204
1224
  }
1205
1225
  }
1226
+ state.eventMetadata = {
1227
+ ...baseMeta,
1228
+ ...promptMeta,
1229
+ properties: baseMeta.properties || promptMeta.properties ? { ...(_a = baseMeta.properties) != null ? _a : {}, ...(_b = promptMeta.properties) != null ? _b : {} } : void 0
1230
+ };
1206
1231
  const userText = textParts.join("\n");
1207
1232
  state.currentInput = userText;
1208
1233
  if (state.parentId) {
@@ -1238,9 +1263,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1238
1263
  });
1239
1264
  await eventShipper.patch(state.currentEventId, {
1240
1265
  isPending: true,
1241
- userId: state.sessionId,
1266
+ userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
1242
1267
  convoId: state.sessionId,
1243
- eventName: config.eventName,
1268
+ eventName: (_f = (_e = state.eventMetadata) == null ? void 0 : _e.eventName) != null ? _f : config.eventName,
1244
1269
  input: userText,
1245
1270
  ...newAttachments.length > 0 ? { attachments: newAttachments } : {},
1246
1271
  properties: {
@@ -1248,8 +1273,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1248
1273
  directory,
1249
1274
  hostname,
1250
1275
  os,
1251
- turn_count: state.turnNumber,
1252
- tool_count: state.toolCount
1276
+ plugin_version: PLUGIN_VERSION,
1277
+ ...(_g = state.eventMetadata) == null ? void 0 : _g.properties
1253
1278
  }
1254
1279
  });
1255
1280
  } catch (err) {
@@ -1260,7 +1285,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1260
1285
  // For child sessions, tool spans are parented directly to the parent task span.
1261
1286
  // ------------------------------------------------------------------
1262
1287
  "tool.execute.before": async (toolInput, _output) => {
1263
- var _a;
1288
+ var _a, _b, _c;
1264
1289
  try {
1265
1290
  const { tool, sessionID, callID } = toolInput;
1266
1291
  const state = sessions.get(sessionID);
@@ -1284,7 +1309,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1284
1309
  ids: liveTaskSpan.ids,
1285
1310
  eventContext: {
1286
1311
  eventId: state.currentEventId,
1287
- userId: state.sessionId,
1312
+ userId: (_c = (_b = state.eventMetadata) == null ? void 0 : _b.userId) != null ? _c : state.sessionId,
1288
1313
  convoId: state.sessionId
1289
1314
  }
1290
1315
  };
@@ -1325,7 +1350,6 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1325
1350
  const { tool, sessionID, callID, args } = toolInput;
1326
1351
  const state = sessions.get(sessionID);
1327
1352
  if (!state || !state.currentEventId) return;
1328
- state.toolCount++;
1329
1353
  if (args === void 0 || args === null)
1330
1354
  throw new Error("tool.execute.after: args is required");
1331
1355
  const toolCallArgs = typeof args === "string" ? args : JSON.stringify(args);
@@ -1379,12 +1403,6 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1379
1403
  if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
1380
1404
  }
1381
1405
  }
1382
- if (!state.parentId) {
1383
- await eventShipper.patch(state.currentEventId, {
1384
- userId: state.sessionId,
1385
- properties: { tool_count: state.toolCount }
1386
- });
1387
- }
1388
1406
  } catch (err) {
1389
1407
  console.log(
1390
1408
  `[raindrop-ai/opencode-plugin] [error] Error in tool.execute.after hook: ${err instanceof Error ? err.message : String(err)}`
@@ -1401,6 +1419,7 @@ async function plugin(input) {
1401
1419
  function appLog(level, message) {
1402
1420
  console.log(`[raindrop-ai/opencode-plugin] [${level}] ${message}`);
1403
1421
  }
1422
+ appLog("info", `Loading ${PLUGIN_NAME} v${PLUGIN_VERSION}`);
1404
1423
  if (!config.writeKey) {
1405
1424
  appLog(
1406
1425
  "warn",
package/dist/index.js CHANGED
@@ -26,14 +26,91 @@ function loadConfig(projectDirectory) {
26
26
  } catch (e) {
27
27
  }
28
28
  }
29
+ let eventMetadata;
30
+ const envMeta = process.env["RAINDROP_EVENT_METADATA"];
31
+ if (envMeta) {
32
+ try {
33
+ eventMetadata = JSON.parse(envMeta);
34
+ } catch (e) {
35
+ }
36
+ }
29
37
  return {
30
38
  writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : merged.write_key) != null ? _b : "",
31
39
  endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai/v1",
32
40
  eventName: (_e = merged.event_name) != null ? _e : "opencode_session",
33
- debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false
41
+ debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false,
42
+ eventMetadata
34
43
  };
35
44
  }
36
45
 
46
+ // package.json
47
+ var package_default = {
48
+ name: "@raindrop-ai/opencode-plugin",
49
+ version: "0.0.6",
50
+ description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
51
+ type: "module",
52
+ main: "dist/index.cjs",
53
+ module: "dist/index.js",
54
+ types: "dist/index.d.ts",
55
+ exports: {
56
+ ".": {
57
+ types: "./dist/index.d.ts",
58
+ import: "./dist/index.js",
59
+ require: "./dist/index.cjs"
60
+ }
61
+ },
62
+ sideEffects: false,
63
+ files: [
64
+ "dist/**"
65
+ ],
66
+ scripts: {
67
+ build: "tsup",
68
+ dev: "tsup --watch",
69
+ clean: "rm -rf dist",
70
+ test: "cd tests && pnpm test"
71
+ },
72
+ peerDependencies: {
73
+ "@opencode-ai/plugin": ">=1.2.0",
74
+ "@opencode-ai/sdk": ">=1.2.0"
75
+ },
76
+ peerDependenciesMeta: {
77
+ "@opencode-ai/sdk": {
78
+ optional: true
79
+ }
80
+ },
81
+ devDependencies: {
82
+ "@raindrop-ai/core": "workspace:*",
83
+ "@opencode-ai/plugin": "^1.2.0",
84
+ "@opencode-ai/sdk": "^1.2.0",
85
+ "@types/node": "^20.11.17",
86
+ tsup: "^8.4.0",
87
+ typescript: "^5.3.3"
88
+ },
89
+ tsup: {
90
+ entry: [
91
+ "src/index.ts"
92
+ ],
93
+ format: [
94
+ "cjs",
95
+ "esm"
96
+ ],
97
+ dts: {
98
+ resolve: true
99
+ },
100
+ clean: true,
101
+ noExternal: [
102
+ "@raindrop-ai/core"
103
+ ]
104
+ },
105
+ publishConfig: {
106
+ access: "public"
107
+ }
108
+ };
109
+
110
+ // src/package-info.ts
111
+ var PLUGIN_NAME = package_default.name;
112
+ var PLUGIN_VERSION = package_default.version;
113
+
37
114
  // ../core/dist/chunk-FTBZHS25.js
38
115
  function getCrypto() {
39
116
  const c = globalThis.crypto;
@@ -617,70 +694,6 @@ var TraceShipper = class {
617
694
  }
618
695
  };
619
696
 
620
- // package.json
621
- var package_default = {
622
- name: "@raindrop-ai/opencode-plugin",
623
- version: "0.0.5",
624
- description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
625
- type: "module",
626
- main: "dist/index.cjs",
627
- module: "dist/index.js",
628
- types: "dist/index.d.ts",
629
- exports: {
630
- ".": {
631
- types: "./dist/index.d.ts",
632
- import: "./dist/index.js",
633
- require: "./dist/index.cjs"
634
- }
635
- },
636
- sideEffects: false,
637
- files: [
638
- "dist/**"
639
- ],
640
- scripts: {
641
- build: "tsup",
642
- dev: "tsup --watch",
643
- clean: "rm -rf dist",
644
- test: "cd tests && yarn test"
645
- },
646
- peerDependencies: {
647
- "@opencode-ai/plugin": ">=1.2.0",
648
- "@opencode-ai/sdk": ">=1.2.0"
649
- },
650
- peerDependenciesMeta: {
651
- "@opencode-ai/sdk": {
652
- optional: true
653
- }
654
- },
655
- devDependencies: {
656
- "@raindrop-ai/core": "*",
657
- "@opencode-ai/plugin": "^1.2.0",
658
- "@opencode-ai/sdk": "^1.2.0",
659
- "@types/node": "^20.11.17",
660
- tsup: "^8.4.0",
661
- typescript: "^5.3.3"
662
- },
663
- tsup: {
664
- entry: [
665
- "src/index.ts"
666
- ],
667
- format: [
668
- "cjs",
669
- "esm"
670
- ],
671
- dts: {
672
- resolve: true
673
- },
674
- clean: true,
675
- noExternal: [
676
- "@raindrop-ai/core"
677
- ]
678
- },
679
- publishConfig: {
680
- access: "public"
681
- }
682
- };
683
-
684
697
  // src/shipper.ts
685
698
  var EventShipper2 = class extends EventShipper {
686
699
  constructor(opts) {
@@ -688,8 +701,8 @@ var EventShipper2 = class extends EventShipper {
688
701
  super({
689
702
  ...opts,
690
703
  sdkName: (_a = opts.sdkName) != null ? _a : "opencode-plugin",
691
- libraryName: (_b = opts.libraryName) != null ? _b : package_default.name,
692
- libraryVersion: (_c = opts.libraryVersion) != null ? _c : package_default.version,
704
+ libraryName: (_b = opts.libraryName) != null ? _b : PLUGIN_NAME,
705
+ libraryVersion: (_c = opts.libraryVersion) != null ? _c : PLUGIN_VERSION,
693
706
  defaultEventName: (_d = opts.defaultEventName) != null ? _d : "opencode_session"
694
707
  });
695
708
  }
@@ -701,7 +714,7 @@ var TraceShipper2 = class extends TraceShipper {
701
714
  ...opts,
702
715
  sdkName: (_a = opts.sdkName) != null ? _a : "opencode-plugin",
703
716
  serviceName: (_b = opts.serviceName) != null ? _b : "raindrop.opencode-plugin",
704
- serviceVersion: (_c = opts.serviceVersion) != null ? _c : package_default.version
717
+ serviceVersion: (_c = opts.serviceVersion) != null ? _c : PLUGIN_VERSION
705
718
  });
706
719
  }
707
720
  enqueue(span) {
@@ -801,8 +814,6 @@ var pendingChildSessionsByCallKey = /* @__PURE__ */ new Map();
801
814
  function createSessionState(sessionId) {
802
815
  return {
803
816
  sessionId,
804
- turnNumber: 0,
805
- toolCount: 0,
806
817
  currentInput: "",
807
818
  outputParts: /* @__PURE__ */ new Map(),
808
819
  reasoningParts: /* @__PURE__ */ new Map(),
@@ -856,7 +867,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
856
867
  // event — session lifecycle + streaming parts
857
868
  // ------------------------------------------------------------------
858
869
  event: async ({ event }) => {
859
- var _a, _b, _c, _d, _e;
870
+ var _a, _b, _c, _d, _e, _f, _g;
860
871
  try {
861
872
  const props = event.properties;
862
873
  const info = props["info"];
@@ -1074,12 +1085,11 @@ type: ${name != null ? name : "UnknownError"}`;
1074
1085
  }
1075
1086
  if (!state.parentId) {
1076
1087
  await eventShipper.finish(state.currentEventId, {
1077
- userId: state.sessionId,
1088
+ userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
1078
1089
  model: modelName,
1079
1090
  output: outputText,
1080
1091
  properties: {
1081
- turn_count: state.turnNumber,
1082
- tool_count: state.toolCount
1092
+ plugin_version: PLUGIN_VERSION
1083
1093
  }
1084
1094
  });
1085
1095
  }
@@ -1117,7 +1127,7 @@ type: ${name != null ? name : "UnknownError"}`;
1117
1127
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
1118
1128
  sessions.delete(String(sessionID));
1119
1129
  } else if (event.type === "session.error") {
1120
- const errorSessionID = (_c = props["sessionID"]) != null ? _c : sessionID;
1130
+ const errorSessionID = (_e = props["sessionID"]) != null ? _e : sessionID;
1121
1131
  if (errorSessionID === void 0 || errorSessionID === "")
1122
1132
  throw new Error("session.error: missing sessionID");
1123
1133
  const state = sessions.get(String(errorSessionID));
@@ -1125,7 +1135,7 @@ type: ${name != null ? name : "UnknownError"}`;
1125
1135
  const errorObj = props["error"];
1126
1136
  if (errorObj == null) throw new Error("session.error: missing error object");
1127
1137
  const errorName = errorObj.name;
1128
- const errorMessage = (_e = (_d = errorObj.data) == null ? void 0 : _d.message) != null ? _e : errorObj.name;
1138
+ const errorMessage = (_g = (_f = errorObj.data) == null ? void 0 : _f.message) != null ? _g : errorObj.name;
1129
1139
  if (errorName == null && errorMessage == null)
1130
1140
  throw new Error("session.error: error object must have name or data.message");
1131
1141
  const errorStr = `${errorMessage != null ? errorMessage : errorName}
@@ -1152,6 +1162,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1152
1162
  // chat.message — user sent a message; start a new turn with a fresh event ID
1153
1163
  // ------------------------------------------------------------------
1154
1164
  "chat.message": async (messageInput, output) => {
1165
+ var _a, _b, _c, _d, _e, _f, _g;
1155
1166
  try {
1156
1167
  const { sessionID } = messageInput;
1157
1168
  let state = sessions.get(sessionID);
@@ -1164,15 +1175,24 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1164
1175
  traceShipper.endSpan(state.currentRootSpan);
1165
1176
  state.currentRootSpan = void 0;
1166
1177
  }
1167
- state.turnNumber++;
1168
1178
  if (output == null) throw new Error("chat.message: output is required");
1169
1179
  const parts = output.parts;
1170
1180
  if (parts == null) throw new Error("chat.message: output.parts is required");
1171
1181
  const textParts = [];
1172
1182
  const newAttachments = [];
1183
+ const baseMeta = { ...config.eventMetadata };
1184
+ const promptMeta = {};
1173
1185
  for (const part of parts) {
1174
1186
  if (part["type"] === "text" && typeof part["text"] === "string") {
1175
1187
  textParts.push(part["text"]);
1188
+ const meta = part["metadata"];
1189
+ if (meta != null && typeof meta === "object") {
1190
+ const m = meta;
1191
+ if (typeof m["userId"] === "string") promptMeta.userId = m["userId"];
1192
+ if (typeof m["eventName"] === "string") promptMeta.eventName = m["eventName"];
1193
+ if (m["properties"] != null && typeof m["properties"] === "object")
1194
+ promptMeta.properties = m["properties"];
1195
+ }
1176
1196
  } else if (part["type"] === "file") {
1177
1197
  const mimeType = part["mediaType"];
1178
1198
  const filename = part["filename"];
@@ -1184,6 +1204,11 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1184
1204
  }
1185
1205
  }
1186
1206
  }
1207
+ state.eventMetadata = {
1208
+ ...baseMeta,
1209
+ ...promptMeta,
1210
+ properties: baseMeta.properties || promptMeta.properties ? { ...(_a = baseMeta.properties) != null ? _a : {}, ...(_b = promptMeta.properties) != null ? _b : {} } : void 0
1211
+ };
1187
1212
  const userText = textParts.join("\n");
1188
1213
  state.currentInput = userText;
1189
1214
  if (state.parentId) {
@@ -1219,9 +1244,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1219
1244
  });
1220
1245
  await eventShipper.patch(state.currentEventId, {
1221
1246
  isPending: true,
1222
- userId: state.sessionId,
1247
+ userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
1223
1248
  convoId: state.sessionId,
1224
- eventName: config.eventName,
1249
+ eventName: (_f = (_e = state.eventMetadata) == null ? void 0 : _e.eventName) != null ? _f : config.eventName,
1225
1250
  input: userText,
1226
1251
  ...newAttachments.length > 0 ? { attachments: newAttachments } : {},
1227
1252
  properties: {
@@ -1229,8 +1254,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1229
1254
  directory,
1230
1255
  hostname,
1231
1256
  os,
1232
- turn_count: state.turnNumber,
1233
- tool_count: state.toolCount
1257
+ plugin_version: PLUGIN_VERSION,
1258
+ ...(_g = state.eventMetadata) == null ? void 0 : _g.properties
1234
1259
  }
1235
1260
  });
1236
1261
  } catch (err) {
@@ -1241,7 +1266,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1241
1266
  // For child sessions, tool spans are parented directly to the parent task span.
1242
1267
  // ------------------------------------------------------------------
1243
1268
  "tool.execute.before": async (toolInput, _output) => {
1244
- var _a;
1269
+ var _a, _b, _c;
1245
1270
  try {
1246
1271
  const { tool, sessionID, callID } = toolInput;
1247
1272
  const state = sessions.get(sessionID);
@@ -1265,7 +1290,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1265
1290
  ids: liveTaskSpan.ids,
1266
1291
  eventContext: {
1267
1292
  eventId: state.currentEventId,
1268
- userId: state.sessionId,
1293
+ userId: (_c = (_b = state.eventMetadata) == null ? void 0 : _b.userId) != null ? _c : state.sessionId,
1269
1294
  convoId: state.sessionId
1270
1295
  }
1271
1296
  };
@@ -1306,7 +1331,6 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1306
1331
  const { tool, sessionID, callID, args } = toolInput;
1307
1332
  const state = sessions.get(sessionID);
1308
1333
  if (!state || !state.currentEventId) return;
1309
- state.toolCount++;
1310
1334
  if (args === void 0 || args === null)
1311
1335
  throw new Error("tool.execute.after: args is required");
1312
1336
  const toolCallArgs = typeof args === "string" ? args : JSON.stringify(args);
@@ -1360,12 +1384,6 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
1360
1384
  if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
1361
1385
  }
1362
1386
  }
1363
- if (!state.parentId) {
1364
- await eventShipper.patch(state.currentEventId, {
1365
- userId: state.sessionId,
1366
- properties: { tool_count: state.toolCount }
1367
- });
1368
- }
1369
1387
  } catch (err) {
1370
1388
  console.log(
1371
1389
  `[raindrop-ai/opencode-plugin] [error] Error in tool.execute.after hook: ${err instanceof Error ? err.message : String(err)}`
@@ -1382,6 +1400,7 @@ async function plugin(input) {
1382
1400
  function appLog(level, message) {
1383
1401
  console.log(`[raindrop-ai/opencode-plugin] [${level}] ${message}`);
1384
1402
  }
1403
+ appLog("info", `Loading ${PLUGIN_NAME} v${PLUGIN_VERSION}`);
1385
1404
  if (!config.writeKey) {
1386
1405
  appLog(
1387
1406
  "warn",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raindrop-ai/opencode-plugin",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Raindrop observability plugin for OpenCode — automatic session/event/span tracing",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -21,7 +21,7 @@
21
21
  "build": "tsup",
22
22
  "dev": "tsup --watch",
23
23
  "clean": "rm -rf dist",
24
- "test": "cd tests && yarn test"
24
+ "test": "cd tests && pnpm test"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "@opencode-ai/plugin": ">=1.2.0",
@@ -33,7 +33,7 @@
33
33
  }
34
34
  },
35
35
  "devDependencies": {
36
- "@raindrop-ai/core": "*",
36
+ "@raindrop-ai/core": "workspace:*",
37
37
  "@opencode-ai/plugin": "^1.2.0",
38
38
  "@opencode-ai/sdk": "^1.2.0",
39
39
  "@types/node": "^20.11.17",