@raindrop-ai/claude-code 0.0.4 → 0.0.5

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/dist/index.d.ts CHANGED
@@ -275,6 +275,9 @@ interface HookPayload {
275
275
  agent_transcript_path?: string;
276
276
  trigger?: string;
277
277
  compact_summary?: string;
278
+ file_path?: string;
279
+ memory_type?: string;
280
+ load_reason?: string;
278
281
  }
279
282
  interface MapperConfig {
280
283
  userId: string;
@@ -283,9 +286,58 @@ interface MapperConfig {
283
286
  customProperties: Record<string, unknown>;
284
287
  }
285
288
  declare function mapHookToRaindrop(payload: HookPayload, config: MapperConfig, eventShipper: EventShipper, traceShipper: TraceShipper): Promise<void>;
289
+ /**
290
+ * Parse command-line args for --append-system-prompt and
291
+ * --append-system-prompt-file flags. Handles both space-separated form
292
+ * (--flag value) and equals form (--flag=value). Supports both flags
293
+ * appearing together (they concatenate).
294
+ *
295
+ * Exported for testing.
296
+ */
297
+ declare function extractAppendSystemPrompt(args: string[]): string | undefined;
286
298
 
287
299
  declare const PACKAGE_NAME = "@raindrop-ai/claude-code";
288
- declare const PACKAGE_VERSION = "0.0.4";
300
+ declare const PACKAGE_VERSION = "0.0.5";
301
+
302
+ interface TranscriptSummary {
303
+ /** Aggregated token usage across all turns */
304
+ totalInputTokens: number;
305
+ totalOutputTokens: number;
306
+ totalCacheReadTokens: number;
307
+ totalCacheCreationTokens: number;
308
+ /** Token usage for the most recent turn only */
309
+ lastTurnInputTokens?: number;
310
+ lastTurnOutputTokens?: number;
311
+ lastTurnCacheReadTokens?: number;
312
+ /** Model used (from most recent assistant message) */
313
+ model?: string;
314
+ /** Service tier */
315
+ serviceTier?: string;
316
+ /** Number of API turns (assistant messages) */
317
+ turnCount: number;
318
+ /** Unique tool names used in the session */
319
+ toolsUsed: string[];
320
+ /** Stop reason from last assistant message */
321
+ stopReason?: string;
322
+ /** Total turn duration in ms (from system entries) */
323
+ totalDurationMs?: number;
324
+ /** Claude Code version */
325
+ codeVersion?: string;
326
+ /** Git branch */
327
+ gitBranch?: string;
328
+ /** Whether thinking/reasoning content was used */
329
+ hasThinking: boolean;
330
+ }
331
+ /**
332
+ * Parse a Claude Code transcript JSONL file and extract a summary.
333
+ * Returns undefined if the file doesn't exist or can't be parsed.
334
+ */
335
+ declare function parseTranscript(transcriptPath: string): TranscriptSummary | undefined;
336
+ /**
337
+ * Convert a TranscriptSummary into a flat properties object
338
+ * suitable for merging into event properties.
339
+ */
340
+ declare function transcriptToProperties(summary: TranscriptSummary): Record<string, unknown>;
289
341
 
290
342
  interface LocalDebuggerResult {
291
343
  /** The resolved base URL (e.g. "http://localhost:5899/v1/"), or null if not detected. */
@@ -308,4 +360,4 @@ declare function detectLocalDebugger(debug: boolean): Promise<LocalDebuggerResul
308
360
  */
309
361
  declare function mirrorEventToLocalDebugger(baseUrl: string, payload: Record<string, unknown>, debug: boolean): void;
310
362
 
311
- export { EventShipper, type HookPayload, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SetupScope, TraceShipper, detectLocalDebugger, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, updateConfig };
363
+ export { EventShipper, type HookPayload, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SetupScope, TraceShipper, type TranscriptSummary, detectLocalDebugger, extractAppendSystemPrompt, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, parseTranscript, transcriptToProperties, updateConfig };
package/dist/index.js CHANGED
@@ -645,7 +645,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
645
645
 
646
646
  // src/package-info.ts
647
647
  var PACKAGE_NAME = "@raindrop-ai/claude-code";
648
- var PACKAGE_VERSION = "0.0.4";
648
+ var PACKAGE_VERSION = "0.0.5";
649
649
 
650
650
  // src/shipper.ts
651
651
  var EventShipper2 = class extends EventShipper {
@@ -741,10 +741,145 @@ function updateConfig(patch) {
741
741
  }
742
742
 
743
743
  // src/event-mapper.ts
744
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
744
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
745
+ import { execSync } from "child_process";
745
746
  import { randomUUID as randomUUID2 } from "crypto";
746
747
  import { tmpdir } from "os";
747
748
  import { join as join2 } from "path";
749
+
750
+ // src/transcript.ts
751
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
752
+ function parseTranscript(transcriptPath) {
753
+ var _a, _b, _c, _d, _e;
754
+ try {
755
+ if (!existsSync2(transcriptPath)) return void 0;
756
+ const content = readFileSync2(transcriptPath, "utf-8");
757
+ const lines = content.split("\n").filter((l) => l.trim());
758
+ const summary = {
759
+ totalInputTokens: 0,
760
+ totalOutputTokens: 0,
761
+ totalCacheReadTokens: 0,
762
+ totalCacheCreationTokens: 0,
763
+ turnCount: 0,
764
+ toolsUsed: [],
765
+ hasThinking: false
766
+ };
767
+ const toolNames = /* @__PURE__ */ new Set();
768
+ let lastUsage;
769
+ for (const line of lines) {
770
+ let entry;
771
+ try {
772
+ entry = JSON.parse(line);
773
+ } catch (e) {
774
+ continue;
775
+ }
776
+ if (entry.type === "system" && entry.subtype === "turn_duration") {
777
+ const durationMs = entry["durationMs"];
778
+ if (typeof durationMs === "number") {
779
+ summary.totalDurationMs = ((_a = summary.totalDurationMs) != null ? _a : 0) + durationMs;
780
+ }
781
+ const version = entry["version"];
782
+ if (typeof version === "string") {
783
+ summary.codeVersion = version;
784
+ }
785
+ const branch = entry["gitBranch"];
786
+ if (typeof branch === "string") {
787
+ summary.gitBranch = branch;
788
+ }
789
+ continue;
790
+ }
791
+ if (entry.type !== "assistant") continue;
792
+ const msg = entry.message;
793
+ if (!msg) continue;
794
+ summary.turnCount++;
795
+ if (msg.model) {
796
+ summary.model = msg.model;
797
+ }
798
+ if (msg.stop_reason) {
799
+ summary.stopReason = msg.stop_reason;
800
+ }
801
+ const entryVersion = entry["version"];
802
+ if (typeof entryVersion === "string") {
803
+ summary.codeVersion = entryVersion;
804
+ }
805
+ const entryBranch = entry["gitBranch"];
806
+ if (typeof entryBranch === "string") {
807
+ summary.gitBranch = entryBranch;
808
+ }
809
+ if (msg.usage) {
810
+ const u = msg.usage;
811
+ summary.totalInputTokens += (_b = u.input_tokens) != null ? _b : 0;
812
+ summary.totalOutputTokens += (_c = u.output_tokens) != null ? _c : 0;
813
+ summary.totalCacheReadTokens += (_d = u.cache_read_input_tokens) != null ? _d : 0;
814
+ summary.totalCacheCreationTokens += (_e = u.cache_creation_input_tokens) != null ? _e : 0;
815
+ if (u.service_tier) {
816
+ summary.serviceTier = u.service_tier;
817
+ }
818
+ lastUsage = u;
819
+ }
820
+ if (Array.isArray(msg.content)) {
821
+ for (const block of msg.content) {
822
+ if (block.type === "tool_use" && block.name) {
823
+ toolNames.add(block.name);
824
+ }
825
+ if (block.type === "thinking") {
826
+ summary.hasThinking = true;
827
+ }
828
+ }
829
+ }
830
+ }
831
+ if (lastUsage) {
832
+ summary.lastTurnInputTokens = lastUsage.input_tokens;
833
+ summary.lastTurnOutputTokens = lastUsage.output_tokens;
834
+ summary.lastTurnCacheReadTokens = lastUsage.cache_read_input_tokens;
835
+ }
836
+ summary.toolsUsed = [...toolNames].sort();
837
+ return summary;
838
+ } catch (e) {
839
+ return void 0;
840
+ }
841
+ }
842
+ function transcriptToProperties(summary) {
843
+ var _a, _b;
844
+ const props = {};
845
+ props["ai.usage.prompt_tokens"] = (_a = summary.lastTurnInputTokens) != null ? _a : summary.totalInputTokens;
846
+ props["ai.usage.completion_tokens"] = (_b = summary.lastTurnOutputTokens) != null ? _b : summary.totalOutputTokens;
847
+ if (summary.lastTurnCacheReadTokens !== void 0) {
848
+ props["ai.usage.cache_read_tokens"] = summary.lastTurnCacheReadTokens;
849
+ }
850
+ props["ai.usage.session_total.prompt_tokens"] = summary.totalInputTokens;
851
+ props["ai.usage.session_total.completion_tokens"] = summary.totalOutputTokens;
852
+ props["ai.usage.session_total.cache_read_tokens"] = summary.totalCacheReadTokens;
853
+ props["ai.usage.session_total.cache_creation_tokens"] = summary.totalCacheCreationTokens;
854
+ if (summary.model) {
855
+ props["ai.model"] = summary.model;
856
+ }
857
+ if (summary.serviceTier) {
858
+ props["ai.service_tier"] = summary.serviceTier;
859
+ }
860
+ if (summary.stopReason) {
861
+ props["ai.stop_reason"] = summary.stopReason;
862
+ }
863
+ if (summary.toolsUsed.length > 0) {
864
+ props["ai.tools_used"] = summary.toolsUsed.join(", ");
865
+ }
866
+ props["ai.turn_count"] = summary.turnCount;
867
+ if (summary.totalDurationMs !== void 0) {
868
+ props["ai.duration_ms"] = summary.totalDurationMs;
869
+ }
870
+ if (summary.codeVersion) {
871
+ props["claude_code.version"] = summary.codeVersion;
872
+ }
873
+ if (summary.gitBranch) {
874
+ props["git.branch"] = summary.gitBranch;
875
+ }
876
+ if (summary.hasThinking) {
877
+ props["ai.has_thinking"] = true;
878
+ }
879
+ return props;
880
+ }
881
+
882
+ // src/event-mapper.ts
748
883
  var MAX_ATTR_LENGTH = 32768;
749
884
  function truncate(value) {
750
885
  if (value === void 0) return void 0;
@@ -788,8 +923,8 @@ function writeState(key, value) {
788
923
  function readState(key) {
789
924
  try {
790
925
  const filePath = statePath(key);
791
- if (!existsSync2(filePath)) return void 0;
792
- return readFileSync2(filePath, "utf-8");
926
+ if (!existsSync3(filePath)) return void 0;
927
+ return readFileSync3(filePath, "utf-8");
793
928
  } catch (e) {
794
929
  return void 0;
795
930
  }
@@ -854,12 +989,15 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
854
989
  cwd: payload.cwd,
855
990
  permission_mode: payload.permission_mode,
856
991
  plugin_version: PACKAGE_VERSION,
992
+ sdk: PACKAGE_NAME,
993
+ sdk_version: PACKAGE_VERSION,
857
994
  ...payload.agent_id ? { agent_id: payload.agent_id } : {},
858
995
  ...payload.agent_type ? { agent_type: payload.agent_type } : {}
859
996
  };
860
997
  switch (payload.hook_event_name) {
861
998
  case "SessionStart":
862
999
  writeState(`model_${payload.session_id}`, (_a = payload.model) != null ? _a : "");
1000
+ tryCaptureAppendSystemPrompt(payload.session_id);
863
1001
  break;
864
1002
  case "UserPromptSubmit":
865
1003
  await handleUserPromptSubmit(payload, convoId, config, baseProperties, eventShipper, traceShipper);
@@ -874,10 +1012,13 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
874
1012
  handlePostToolUseFailure(payload, getCurrentEventId(payload.session_id), traceShipper);
875
1013
  break;
876
1014
  case "Stop":
877
- await handleStop(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
1015
+ await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper);
878
1016
  break;
879
1017
  case "StopFailure":
880
- await handleStopFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
1018
+ await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper, {
1019
+ error: payload.error,
1020
+ error_details: payload.error_details
1021
+ });
881
1022
  break;
882
1023
  case "SubagentStart":
883
1024
  handleSubagentStart(payload, getCurrentEventId(payload.session_id), traceShipper);
@@ -888,6 +1029,9 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
888
1029
  case "PermissionDenied":
889
1030
  handlePermissionDenied(payload, getCurrentEventId(payload.session_id), traceShipper);
890
1031
  break;
1032
+ case "InstructionsLoaded":
1033
+ handleInstructionsLoaded(payload);
1034
+ break;
891
1035
  case "PostCompact":
892
1036
  await handlePostCompact(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
893
1037
  break;
@@ -896,6 +1040,8 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
896
1040
  deleteState(turnEventKey(payload.session_id));
897
1041
  deleteState(rootSpanKey(payload.session_id));
898
1042
  deleteState(`model_${payload.session_id}`);
1043
+ deleteState(appendSystemPromptKey(payload.session_id));
1044
+ cleanupInstructions(payload.session_id);
899
1045
  break;
900
1046
  default:
901
1047
  if (config.debug) {
@@ -1053,25 +1199,243 @@ async function handlePostCompact(payload, eventId, config, properties, eventShip
1053
1199
  }
1054
1200
  });
1055
1201
  }
1056
- async function handleStop(payload, eventId, config, properties, eventShipper) {
1057
- await eventShipper.patch(eventId, {
1058
- isPending: false,
1059
- userId: config.userId,
1060
- eventName: config.eventName,
1061
- output: payload.last_assistant_message,
1062
- properties
1063
- });
1202
+ function handleInstructionsLoaded(payload) {
1203
+ var _a;
1204
+ if (!payload.file_path || !payload.session_id) return;
1205
+ try {
1206
+ if (existsSync3(payload.file_path)) {
1207
+ const content = readFileSync3(payload.file_path, "utf-8");
1208
+ const MAX_INSTRUCTIONS_LENGTH = 8192;
1209
+ const truncated = content.length > MAX_INSTRUCTIONS_LENGTH ? content.slice(0, MAX_INSTRUCTIONS_LENGTH) + "\n...[truncated]" : content;
1210
+ const fileKey = safeKey(payload.file_path);
1211
+ const header = `# ${(_a = payload.memory_type) != null ? _a : "instructions"}: ${payload.file_path}
1212
+ `;
1213
+ writeState(
1214
+ `instr_${payload.session_id}_${fileKey}`,
1215
+ header + truncated
1216
+ );
1217
+ }
1218
+ } catch (e) {
1219
+ }
1064
1220
  }
1065
- async function handleStopFailure(payload, eventId, config, properties, eventShipper) {
1221
+ function gatherInstructions(sessionId) {
1222
+ try {
1223
+ if (!existsSync3(STATE_DIR)) return void 0;
1224
+ const prefix = safeKey(`instr_${sessionId}_`);
1225
+ const files = readdirSync(STATE_DIR).filter((f) => f.startsWith(prefix));
1226
+ if (files.length === 0) return void 0;
1227
+ const parts = [];
1228
+ for (const file of files.sort()) {
1229
+ try {
1230
+ parts.push(readFileSync3(join2(STATE_DIR, file), "utf-8"));
1231
+ } catch (e) {
1232
+ continue;
1233
+ }
1234
+ }
1235
+ return parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
1236
+ } catch (e) {
1237
+ return void 0;
1238
+ }
1239
+ }
1240
+ function cleanupInstructions(sessionId) {
1241
+ try {
1242
+ if (!existsSync3(STATE_DIR)) return;
1243
+ const prefix = safeKey(`instr_${sessionId}_`);
1244
+ for (const file of readdirSync(STATE_DIR).filter((f) => f.startsWith(prefix))) {
1245
+ try {
1246
+ unlinkSync(join2(STATE_DIR, file));
1247
+ } catch (e) {
1248
+ }
1249
+ }
1250
+ } catch (e) {
1251
+ }
1252
+ }
1253
+ var APPEND_FLAG = "--append-system-prompt";
1254
+ var APPEND_FILE_FLAG = "--append-system-prompt-file";
1255
+ var MAX_ANCESTOR_DEPTH = 15;
1256
+ var PS_TIMEOUT_MS = 3e3;
1257
+ function appendSystemPromptKey(sessionId) {
1258
+ return `append_sysprompt_${sessionId}`;
1259
+ }
1260
+ function tryCaptureAppendSystemPrompt(sessionId) {
1261
+ try {
1262
+ const content = readAncestorAppendSystemPrompt();
1263
+ if (content) {
1264
+ writeState(appendSystemPromptKey(sessionId), content);
1265
+ }
1266
+ } catch (e) {
1267
+ }
1268
+ }
1269
+ function readAncestorAppendSystemPrompt() {
1270
+ let pid;
1271
+ try {
1272
+ pid = process.ppid;
1273
+ } catch (e) {
1274
+ return void 0;
1275
+ }
1276
+ if (!pid || pid <= 1) return void 0;
1277
+ for (let depth = 0; depth < MAX_ANCESTOR_DEPTH && pid != null && pid > 1; depth++) {
1278
+ try {
1279
+ const args = getProcessArgs(pid);
1280
+ if (args && args.length > 0) {
1281
+ const content = extractAppendSystemPrompt(args);
1282
+ if (content) return content;
1283
+ }
1284
+ } catch (e) {
1285
+ }
1286
+ try {
1287
+ pid = getParentPid(pid);
1288
+ } catch (e) {
1289
+ break;
1290
+ }
1291
+ }
1292
+ return void 0;
1293
+ }
1294
+ function getProcessArgs(pid) {
1295
+ if (process.platform === "linux") {
1296
+ try {
1297
+ const raw = readFileSync3(`/proc/${pid}/cmdline`, "utf-8");
1298
+ const args = raw.split("\0").filter(Boolean);
1299
+ return args.length > 0 ? args : void 0;
1300
+ } catch (e) {
1301
+ }
1302
+ }
1303
+ try {
1304
+ const output = execSync(`ps -ww -o args= -p ${pid}`, {
1305
+ encoding: "utf-8",
1306
+ timeout: PS_TIMEOUT_MS,
1307
+ stdio: ["ignore", "pipe", "ignore"]
1308
+ });
1309
+ const trimmed = output.trim();
1310
+ if (!trimmed) return void 0;
1311
+ return trimmed.split(/\s+/);
1312
+ } catch (e) {
1313
+ return void 0;
1314
+ }
1315
+ }
1316
+ function getParentPid(pid) {
1317
+ try {
1318
+ const output = execSync(`ps -o ppid= -p ${pid}`, {
1319
+ encoding: "utf-8",
1320
+ timeout: PS_TIMEOUT_MS,
1321
+ stdio: ["ignore", "pipe", "ignore"]
1322
+ });
1323
+ const ppid = parseInt(output.trim(), 10);
1324
+ return Number.isFinite(ppid) && ppid > 0 ? ppid : void 0;
1325
+ } catch (e) {
1326
+ return void 0;
1327
+ }
1328
+ }
1329
+ function extractAppendSystemPrompt(args) {
1330
+ const parts = [];
1331
+ for (let i = 0; i < args.length; i++) {
1332
+ const arg = args[i];
1333
+ try {
1334
+ if (arg === APPEND_FILE_FLAG && i + 1 < args.length) {
1335
+ const filePath = args[i + 1];
1336
+ i++;
1337
+ try {
1338
+ if (existsSync3(filePath)) {
1339
+ parts.push(readFileSync3(filePath, "utf-8"));
1340
+ }
1341
+ } catch (e) {
1342
+ }
1343
+ continue;
1344
+ }
1345
+ if (arg.startsWith(APPEND_FILE_FLAG + "=")) {
1346
+ const filePath = arg.slice(APPEND_FILE_FLAG.length + 1);
1347
+ try {
1348
+ if (filePath && existsSync3(filePath)) {
1349
+ parts.push(readFileSync3(filePath, "utf-8"));
1350
+ }
1351
+ } catch (e) {
1352
+ }
1353
+ continue;
1354
+ }
1355
+ if (arg === APPEND_FLAG && i + 1 < args.length) {
1356
+ const valueParts = [];
1357
+ for (let j = i + 1; j < args.length; j++) {
1358
+ if (args[j].startsWith("--")) break;
1359
+ valueParts.push(args[j]);
1360
+ i = j;
1361
+ }
1362
+ if (valueParts.length > 0) {
1363
+ parts.push(valueParts.join(" "));
1364
+ }
1365
+ continue;
1366
+ }
1367
+ if (arg.startsWith(APPEND_FLAG + "=") && !arg.startsWith(APPEND_FILE_FLAG)) {
1368
+ const value = arg.slice(APPEND_FLAG.length + 1);
1369
+ if (value) {
1370
+ parts.push(value);
1371
+ }
1372
+ continue;
1373
+ }
1374
+ } catch (e) {
1375
+ continue;
1376
+ }
1377
+ }
1378
+ return parts.length > 0 ? parts.join("\n") : void 0;
1379
+ }
1380
+ function readAppendSystemPrompt(sessionId) {
1381
+ try {
1382
+ return readState(appendSystemPromptKey(sessionId)) || void 0;
1383
+ } catch (e) {
1384
+ return void 0;
1385
+ }
1386
+ }
1387
+ function enrichFromTranscript(payload, eventId, traceShipper) {
1388
+ var _a, _b;
1389
+ if (!payload.transcript_path) {
1390
+ return { summary: void 0, props: {} };
1391
+ }
1392
+ try {
1393
+ const summary = parseTranscript(payload.transcript_path);
1394
+ if (!summary) return { summary: void 0, props: {} };
1395
+ const props = transcriptToProperties(summary);
1396
+ const spanInputTokens = (_a = summary.lastTurnInputTokens) != null ? _a : summary.totalInputTokens;
1397
+ const spanOutputTokens = (_b = summary.lastTurnOutputTokens) != null ? _b : summary.totalOutputTokens;
1398
+ if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
1399
+ const parent = getParentContext(payload);
1400
+ const now = nowUnixNanoString();
1401
+ traceShipper.createSpan({
1402
+ name: summary.model,
1403
+ eventId,
1404
+ parent,
1405
+ startTimeUnixNano: now,
1406
+ endTimeUnixNano: now,
1407
+ attributes: [
1408
+ attrString("ai.operationId", "generateText"),
1409
+ attrString("gen_ai.response.model", summary.model),
1410
+ attrString("gen_ai.system", "anthropic"),
1411
+ attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
1412
+ attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
1413
+ ...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
1414
+ ]
1415
+ });
1416
+ }
1417
+ return { summary, props };
1418
+ } catch (e) {
1419
+ return { summary: void 0, props: {} };
1420
+ }
1421
+ }
1422
+ async function handleStopOrFailure(payload, eventId, config, properties, eventShipper, traceShipper, extraProperties) {
1423
+ const { summary, props: transcriptProps } = enrichFromTranscript(payload, eventId, traceShipper);
1424
+ const instructions = gatherInstructions(payload.session_id);
1425
+ const appendSysPrompt = readAppendSystemPrompt(payload.session_id);
1066
1426
  await eventShipper.patch(eventId, {
1067
1427
  isPending: false,
1068
1428
  userId: config.userId,
1429
+ convoId: payload.session_id,
1069
1430
  eventName: config.eventName,
1070
1431
  output: payload.last_assistant_message,
1432
+ ...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
1071
1433
  properties: {
1072
1434
  ...properties,
1073
- error: payload.error,
1074
- error_details: payload.error_details
1435
+ ...transcriptProps,
1436
+ ...instructions ? { system_instructions: truncate(instructions) } : {},
1437
+ ...appendSysPrompt ? { append_system_prompt: truncate(appendSysPrompt) } : {},
1438
+ ...extraProperties
1075
1439
  }
1076
1440
  });
1077
1441
  }
@@ -1088,7 +1452,7 @@ async function handleSessionEnd(payload, eventId, config, properties, eventShipp
1088
1452
  }
1089
1453
 
1090
1454
  // src/local-debugger.ts
1091
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1455
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1092
1456
  import { tmpdir as tmpdir2 } from "os";
1093
1457
  import { join as join3 } from "path";
1094
1458
  var DEFAULT_PORT = 5899;
@@ -1099,8 +1463,8 @@ var CACHE_FILE = join3(CACHE_DIR, "debugger_cache");
1099
1463
  var CACHE_TTL_MS = 5e3;
1100
1464
  function readCache() {
1101
1465
  try {
1102
- if (!existsSync3(CACHE_FILE)) return void 0;
1103
- const data = JSON.parse(readFileSync3(CACHE_FILE, "utf-8"));
1466
+ if (!existsSync4(CACHE_FILE)) return void 0;
1467
+ const data = JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
1104
1468
  if (Date.now() - data.ts > CACHE_TTL_MS) return void 0;
1105
1469
  return data;
1106
1470
  } catch (e) {
@@ -1173,9 +1537,12 @@ export {
1173
1537
  PACKAGE_VERSION,
1174
1538
  TraceShipper2 as TraceShipper,
1175
1539
  detectLocalDebugger,
1540
+ extractAppendSystemPrompt,
1176
1541
  getConfigPath,
1177
1542
  loadConfig,
1178
1543
  mapHookToRaindrop,
1179
1544
  mirrorEventToLocalDebugger,
1545
+ parseTranscript,
1546
+ transcriptToProperties,
1180
1547
  updateConfig
1181
1548
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raindrop-ai/claude-code",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Raindrop observability for Claude Code CLI — automatic session, tool call, and prompt tracing via hooks",
5
5
  "license": "MIT",
6
6
  "type": "module",